Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 << zurück
Visual C# 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2005

Visual C# 2005
1.320 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-586-X
gp Kapitel 4 Das Klassendesign (Teil 1)
  gp 4.1 Einführung in die Objektorientierung
    gp 4.1.1 Das objektorientierte Paradigma
    gp 4.1.2 Vorteile der objektorientierten Programmierung
    gp 4.1.3 Zusammenfassung
  gp 4.2 Die Klassendefinition
    gp 4.2.1 Die Deklaration von Objektvariablen
    gp 4.2.2 Zugriffsmodifizierer einer Klasse
    gp 4.2.3 Der Projekttyp »Klassenbibliothek«
    gp 4.2.4 Splitten einer Klassendefinition mit »partial«
    gp 4.2.5 Zusammenfassung
  gp 4.3 Objektmethoden
    gp 4.3.1 Der Aufruf einer Methode
    gp 4.3.2 Methoden mit Parameterliste
    gp 4.3.3 Rückgabewert einer Methode
    gp 4.3.4 Variablen in einer Methode
    gp 4.3.5 Zugriffsmodifizierer einer Methode
    gp 4.3.6 Besondere Aspekte einer Parameterliste
    gp 4.3.7 Referenz- und Wertparameter
    gp 4.3.8 Methodenüberladung
    gp 4.3.9 Aufruf überladener Methoden mit impliziter Konvertierung
    gp 4.3.10 Zusammenfassung
  gp 4.4 Objekteigenschaften
    gp 4.4.1 Datenkapselung mit Eigenschaftsmethoden sicherstellen
    gp 4.4.2 Die Ergänzung der Klasse »Circle«
    gp 4.4.3 Lese- und schreibgeschützte Eigenschaften
    gp 4.4.4 Sichtbarkeit der Accessoren »get« und »set«
    gp 4.4.5 Konstanten
    gp 4.4.6 Methode oder Eigenschaft?
    gp 4.4.7 Die Trennung von Daten und Code
    gp 4.4.8 Der Zugriff auf private Daten
    gp 4.4.9 Zusammenfassung
  gp 4.5 Konstruktoren
    gp 4.5.1 Die Konstruktoren in der Klasse »Circle«
    gp 4.5.2 Die Konstruktoraufrufe
    gp 4.5.3 Definition von Konstruktoren
    gp 4.5.4 »internal«-Konstruktoren
    gp 4.5.5 »private«-Konstruktoren
    gp 4.5.6 Konstruktorverkettung
    gp 4.5.7 Zusammenfassung
  gp 4.6 Der Destruktor
    gp 4.6.1 Das Zerstören von Objekten
    gp 4.6.2 Der Garbage Collector
    gp 4.6.3 Die Bereitstellung eines Destruktors
    gp 4.6.4 Das Zerstören eines Objekts
    gp 4.6.5 Die »Dispose«-Methode
    gp 4.6.6 Der Garbage Collector in Aktion
    gp 4.6.7 Zusammenfassung
  gp 4.7 Arbeiten mit Objektreferenzen
    gp 4.7.1 Prüfen auf Initialisierung
    gp 4.7.2 Die Deklaration vom Typ »Object«
    gp 4.7.3 Mehrere Referenzen auf ein Objekt
    gp 4.7.4 Den Typ einer Objektreferenz mit »ToString« ermitteln
    gp 4.7.5 Der »is«-Operator zur Typfeststellung
    gp 4.7.6 Referenzvergleiche
    gp 4.7.7 Das Klonen von Objekten
    gp 4.7.8 Zusammenfassung


Galileo Computing

4.4 Objekteigenschaften  downtop

Eigenschaften beschreiben den Zustand eines Objekts und sind das individuelle Charakteristikum, um ein bestimmtes Objekt von einem anderen desselben Typs zu unterscheiden.

Betrachten wir den einfachsten Fall der Eigenschaftsimplementierung am Beispiel der Klasse Circle. Ein Kreis wird zumindest durch seinen Radius beschrieben. Dem haben wir mit unserer Implementierung Rechnung getragen. Außerdem sollte die Lage des Kreismittelpunktes bekannt sein, um ein Kreisobjekt positionieren zu können. Daher werden in der Klassendefinition neben dem Radius zwei weitere Felder festgelegt.


public class Circle {
  public int XKoordinate;
  public int YKoordinate;
  public double Radius;
  // Instanzmethoden
  ...
}

Innerhalb eines Anweisungsblocks deklarierte Variablen gelten als nicht initialisiert, solange ihnen kein Wert zugewiesen wird. Ein Feld, das definitionsgemäß innerhalb einer Klasse, aber außerhalb des Anweisungsblocks einer Methode deklariert ist, hat von Anfang an einen Standardinitialisierungswert. Der folgenden Tabelle können Sie diese für die Werttypen entnehmen.


Tabelle 4.3   Standardinitialisierungswerte der Werttypen

Werttyp Standardwert
bool false
byte 0
char ‘\0’
decimal 0.0
double 0.0
float 0.0
int 0
long 0
short 0

Aus Gründen der besseren Lesbarkeit des Programmcodes ist es jedoch nicht unüblich, bei Feldern explizit den Standardwert anzugeben. Das führt uns zu den folgenden Felddefinitionen in der Klasse Circle:


public class Circle {
  public int XKoordinate = 0;
  public int YKoordinate = 0;
  public double Radius = 0;
  // Instanzmethoden
  ...
}


Galileo Computing

4.4.1 Datenkapselung mit Eigenschaftsmethoden sicherstellen  downtop

Eine auf ein Objekt der Klasse Circle zugreifende Methode kann die Eigenschaften direkt manipulieren bzw. die Inhalte auswerten, da diese public deklariert sind. Im folgenden Beispielcode wird das gezeigt.


static void Main(string[] args) {
  Circle kreis = new Circle();
  Console.Write("Geben Sie den Radius ein: ");
  kreis.Radius = Convert.ToDouble(Console.ReadLine());
  Console.Write("Geben Sie die X-Koordinate ein: ");
  kreis.XKoordinate = Convert.ToInt32(Console.ReadLine());
  Console.Write("Geben Sie die Y-Koordinate ein: ");
  kreis.YKoordinate = Convert.ToInt32(Console.ReadLine());
  Console.WriteLine();
  Console.Write("------------------------------------\n");
  Console.Write("Radius       = {0}\n",kreis.Radius);
  Console.Write("X-Koordinate = {0}\n",kreis.XKoordinate);
  Console.Write("Y-Koordinate = {0}\n",kreis.YKoordinate);
  Console.ReadLine();
}

Zu den Schlüsselkonzepten der objektorientierten Programmierung zählt die Datenkapselung. Danach sollten Objektdaten immer vor dem direkten Zugriff durch einen Aufrufer geschützt werden. Die bisherige Implementierung der Felder Radius, XKoordinate und YKoordinate entspricht nicht diesem Paradigma, da alle Felder direkt von außen verändert werden können, ohne dass dieser Vorgang durch die Klasse kontrolliert wird.

Denkt man über den Sinn eines Feldes nach, wird man sehr schnell zu der Erkenntnis gelangen, dass öffentlich deklarierte Variablen in einer Klasse, also Felder, nicht mehr darstellen als Platzhalter für Daten eines bestimmten Typs. Von einer Objekteigenschaft wird aber meistens deutlich mehr erwartet – nämlich Intelligenz. Was würden Sie verlangen, wenn in der Klasse Circle das Feld Farbe beispielsweise mit


class Circle {
  public Color Farbe;
  ...
}

definiert wäre und Sie einem konkreten Objekt mit


Circle meinKreis = new Circle();
meinKreis.Farbe = Rot

den Farbwert Rot übergeben? Selbstverständlich würden Sie davon ausgehen, dass sich das Circle-Objekt auch in dieser Farbe dargestellt. Aber die Fähigkeit, den Kreis rot zu zeichnen und nicht blau, lila oder schwarz, hätte das öffentlich deklarierte Feld dieser Klasse nicht.

Es gibt einen weiteren Gesichtspunkt, dem wir bisher noch keine Aufmerksamkeit geschenkt haben: Was ist, wenn der Aufrufer bei der Zuweisung an den Radius eine negative Zahl übergibt? Sicherlich wird ein pfiffiger Mathematiker oder Physiker einen akademischen Erklärungsversuch starten, an dem wir uns aber hier nicht beteiligen wollen. Was müssen wir also tun, um die Bedingung

CircleObjekt.Radius >= 0

zu erfüllen?

Denkansätze zur Lösung dieses Problems gibt es mehrere:

gp  Datentypenänderung des Feldes »Radius«: Eine erste Idee könnte sein, einen Datentypen zu wählen, der nur den positiven Zahlenbereich abdeckt. Eine solche Lösung ist jedoch schlecht, da diese Datentypen bis auf byte nicht CLS-konform sind.
gp  Eingabeüberprüfung beim Aufrufer: Die Eingabe könnte vor der Zuweisung mit einer if-Anweisung auf die gestellte Bedingung hin geprüft werden. Dieser Lösungsansatz erfüllt zwar die Forderung, ist aber dennoch objektiv betrachtet nicht akzeptabel – insbesondere dann, wenn an vielen Stellen im Programmcode dem Radius ein Wert übergeben werden kann.
gp  Eingabeüberprüfung im Klassencode: Nach der Argumentation in den vorhergehenden beiden Punkten verbleibt nur noch eine Alternative, die auch den besten Lösungsansatz darstellt: die Prüfung der Eingabe im Klassencode.

Da wir festgestellt haben, dass die öffentliche Deklaration eines Feldes aus verschiedenen Gründen nicht akzeptabel ist, muss eine Methode herangezogen werden. C# bietet zu diesem Zweck Eigenschaftsmethoden mit den Accessoren get und set an, über die sowohl die Zuweisung als auch die Auswertung eines Feldes erfolgt. Die Syntax einer Eigenschaftsmethode lautet wie folgt:


// Struktur einer Eigenschaftsprozedur
[Modifizierer] Datentyp Bezeichner
{
   [Modifizierer] get
   {
      // Anweisungen
      return Rückgabewert;
   }
   [Modifizierer] set
   {
      // Anweisungen
   }
}

Eine Eigenschaftsdefinition ähnelt einer Variablendeklaration. Das ansonsten übliche, abschließende Semikolon bei einer Variablendeklaration wird jetzt durch einen Anweisungsblock ersetzt, in dem die beiden Accessoren get und set definiert sind, die ihrerseits einen eigenen Anweisungsblock beschreiben. Sowohl die Eigenschaftsmethode als auch deren untergeordnete Accessoren können optional einen Zugriffsmodifizierer zur Beschreibung der Sichtbarkeit aufweisen.

Sehen wir uns zunächst die vollständige Implementierung der Eigenschaftsmethode Radius an, welche die Forderung erfüllt, die Zuweisung eines negativen Radius an ein Kreisobjekt zu verhindern und gleichzeitig einen zulässigen Wert zu speichern:


public class Circle {
  private double radius = 0;
  // Eigenschaftsmethode
  public double Radius {
    get { return radius; }
    set {
      if (value >= 0)
        // bei einer Wertzuweisung wird der übergebene Wert im
        // impliziten Parameter value entgegengenommen
        radius = value;
      else
        Console.Write("Unzulässiger Wert.");
    }
  }
  ...
}

Der Wert, den eine Eigenschaft repräsentiert, muss im Objekt dauerhaft gespeichert werden. Die Deklaration einer lokalen Variablen innerhalb der Eigenschaft kommt nicht in Frage, weil der Inhalt nach dem Beenden des Aufrufs der Eigenschaftsmethode verloren gehen würde. Um diese Schwierigkeit zu meistern, wird der Eigenschaftswert in einem Feld gespeichert, das private deklariert ist und sich folglich im Sichtbarkeitsbereich der Methode befindet. Gleichzeitig wird ein unzulässiger, direkt lesender bzw. schreibender Zugriff von außerhalb verhindert. Der Wert des Radius ist gekapselt.

Der Ersatz des zuvor öffentlich deklarierten Feldes durch ein privates, auf das über die Eigenschaftsmethode zugegriffen wird, kann sich zu einem späteren Zeitpunkt als großer Vorteil herausstellen. Wenn sich die Implementierung des Programmcodes innerhalb der Eigenschaft als fehlerhaft erweist (das soll es schließlich auch geben) oder zu einem späteren Zeitpunkt aktualisiert werden muss, um sich an veränderte Rahmenbedingungen anzupassen (beispielsweise weil der Radius eines Kreisobjekts einen bestimmten Maximalwert nicht überschreiten darf), wird der Aufrufer die Korrektur bzw. Änderung nicht bemerken. Er erkennt nur den für ihn sichtbaren öffentlichen Teil der Schnittstelle, nämlich den Bezeichner und den Datentyp, die beide unverändert bleiben. Die Implementierung des Programmcodes selbst ist aber nicht Bestandteil der öffentlichen Schnittstelle, sie bleibt im Verborgenen. Eigenschaften garantieren also nicht nur die Kapselung der Objektdaten, sondern auch eine Anpassung des Objekts an veränderte Bedingungen.

Wird einer Eigenschaft mit


meinKreis.Radius = 10;

ein Wert zugewiesen, wird der set-Block innerhalb der Eigenschaftsmethode aufgerufen. Der zugewiesene Wert wird in einem impliziten Parameter empfangen, der grundsätzlich value lautet. Der Datentyp von value entspricht dem Datentyp der Eigenschaft. In unserem Beispiel ist value demnach vom Typ double. Innerhalb des set-Blocks können Anweisungen programmiert werden, die den zu übergebenden Wert auf seine Zulässigkeit hin überprüfen und/oder den Wert dazu benutzen, damit Operationen auszuführen.

Beachten Sie, dass die zuvor benutzte Großschreibweise des Feldes Radius nun in radius geändert wurde, da das Feld nur noch in der Klasse sichtbar ist. Die Eigenschaftsmethode hingegen gehört zur öffentlichen Schnittstelle der Klasse, ist für den Aufrufer sichtbar und beginnt deshalb konventionsgemäß mit einem Großbuchstaben.


Hinweis   Es ist allgemein üblich, private Klassenmitglieder mit einem Kleinbuchstaben zu beginnen und alle öffentlichen mit einem Großbuchstaben.

Das Auswerten der Eigenschaft seitens des Aufrufers mit


Variable = meinKreis.Radius;

hat den Aufruf des get-Blocks der Eigenschaftsmethode zur Folge. Im einfachsten Fall steht im get-Block nur eine return-Anweisung, die den Inhalt des gekapselten Feldes an den Aufrufer zurückgibt.


Galileo Computing

4.4.2 Die Ergänzung der Klasse »Circle«  downtop

In ähnlicher Weise, wie wir die Eigenschaft Radius implementiert haben, sollten wir auch die beiden öffentlichen Felder XKoordinate und YKoordinate durch Eigenschaftsmethoden ersetzen.


public class Circle {
  // ---------- Felddefinition -------------
  private int xKoordinate = 0;
  private int yKoordinate = 0;
  ...
  public int XKoordinate {
    get { return xKoordinate; }
    set { xKoordinate = value; }
  }
  public int YKoordinate {
    get { return yKoordinate; }
    set { yKoordinate = value; }
  }
}


Galileo Computing

4.4.3 Lese- und schreibgeschützte Eigenschaften  downtop

Es kommt häufig vor, dass eine Eigenschaft entweder schreib- oder lesegeschützt implementiert werden muss. Die Realisierung ist denkbar einfach: Sie erstellen eine schreibgeschützte Eigenschaft ohne set-Block. Eine so definierte Eigenschaft kann nur über get ausgewertet werden.


...
private int myProp = 0;
...
public int MyProp {
  get {return myProp;}
}
...

Ein Benutzer der Klasse kann der schreibgeschützten Eigenschaft MyProp durch eine herkömmliche Zuweisung keinen Wert übergeben, daher muss es einen anderen Weg geben. Dieser führt in der Regel über den Aufruf einer anderen Methode der Klasse, die ihrerseits den Wert nach einem vorgegebenen Algorithmus setzt. Häufig werden die Werte gekapselter Felder von schreibgeschützten Eigenschaften bei der Initialisierung des Objekts im Konstruktor festgelegt.

Soll eine Objekteigenschaft zur Laufzeit einer Anwendung lesegeschützt sein, darf die Implementierung der Eigenschaft nur den set-Block enthalten.


...
private int myProp = 0;
...
public int MyProp {
  set {myProp = value;}
}
...

Der Wert einer lesegeschützten Eigenschaft kann selbstverständlich durch eine andere Instanzmethode zurückgegeben werden, die das gekapselte Feld, hier myProp, auswertet.


Galileo Computing

4.4.4 Sichtbarkeit der Accessoren »get« und »set«  downtop

Wird keine andere Angabe gemacht, entspricht die Sichtbarkeit der beiden Accessoren get und set per Vorgabe der Sichtbarkeit der Eigenschaftsmethode. Ist die Eigenschaftsmethode beispielsweise public, sind get und set automatisch ebenfalls ohne Einschränkung sichtbar. Mit einem neuen Feature wartet C# seit der Version 2.0 auf: Die Accessoren dürfen eine individuelle Sichtbarkeit aufweisen. Damit lässt sich der Zugriff auf die beiden Accessoren feiner steuern.


public int MyProp {
  internal get {
    return this.someValue;
  }
  set {
    this.someValue = value;
  }
}

In diesem Codefragment ist die Eigenschaft MyProp öffentlich definiert. Der set-Accessor hat keinen abweichenden Zugriffsmodifizierer, folglich kann aus jeder anderen Anwendung heraus der Wert gesetzt werden. Im Gegensatz dazu schränkt der Zugriffsmodifizierer internal das Auswerten der Eigenschaft auf die aktuelle Anwendung ein. Code, der sich nicht in der gleichen Anwendung befindet, kann den Inhalt des Feldes someValue nicht auswerten.

Der Einsatz der Zugriffsmodifizierer auf die beiden Accessoren unterliegt Regeln, die Sie sich merken sollten:

gp  In der Eigenschaftsmethode müssen beide Accessoren definiert sein.
gp  Nur bei einem der beiden Accessoren darf ein vom Zugriffsmodifizierer der Eigenschaftsmethode abweichender Zugriffsmodifizierer angegeben werden.
gp  Der Zugriffsmodifizierer des Accessors muss einschränkender sein als der der Eigenschaftsmethode. Angenommen die Eigenschaftsmethode ist public definiert, so können die Modifizierer internal, protected und private benutzt werden. public wäre in diesem Fall sogar unzulässig.

Galileo Computing

4.4.5 Konstanten  downtop

Benötigen Sie einen Wert, der während der Laufzeit der Anwendung nicht geändert werden darf, sollten Sie eine Konstante deklarieren. Die allgemeine Syntax wird folgendermaßen beschrieben:


Zugriffsmodifizierer const Datentyp Bezeichner = Wert;

Kennzeichnend für eine Konstante ist das Schlüsselwort const zwischen dem Zugriffsmodifizierer und dem Datentyp, beispielsweise:


public class Circle {
  public const double PI = 3.14;
  ...
}

Wie Sie später noch erfahren, ist eine Konstante ein statisches Klassenmitglied, woraus eine wichtige Konsequenz folgt: Um eine Konstante auszuwerten, muss der Name der Klasse, in der die Konstante definiert ist, angegeben werden, also beispielsweise:


Circle.PI;

Der Zugriff über die Referenz auf ein Objekt dieser Klasse mit


Circle meinKreis = new Circle();
// Achtung: unerlaubter Zugriff
double x = meinKreis.PI;

ist nicht erlaubt.

Der Wert einer Konstanten wird schon bei der Kompilierung ausgewertet, daher kommen fast nur Werttypen wie int, long, double usw. in Frage, denn Referenztypen können erst zur Laufzeit aufgelöst werden. Eine Ausnahme bildet nur der Typ string, der unter .NET zwar als Referenztyp behandelt wird, aber dennoch als Konstante angegeben werden darf.

Schreibgeschützte Felder mit »readonly«

Durch die Eliminierung des set-Blocks in einer Eigenschaft können Sie diese vor unbefugtem schreibenden Zugriff schützen. Dieses Verhalten kann auch innerhalb einer Felddefinition erreicht werden. Dazu wird die Deklaration um den Modifikator readonly ergänzt, z.B.:


public readonly double PI = 3.14;

Unterscheidungsmerkmale zwischen »const«- und »readonly«-Konstanten

Konstantendefinitionen mit const und readonly unterscheiden sich in zwei wichtigen Punkten:

gp  Die Zuweisung eines Wertes an ein readonly-Feld kann sowohl bei der Deklaration des Feldes als auch innerhalb eines Konstruktors erfolgen. Das Festlegen des Wertes in einer Methode ist jedoch nicht erlaubt. Daher eignet sich ein readonly-Feld insbesondere dann, wenn ein Referenztyp erforderlich ist, der erst zur Laufzeit zur Verfügung steht.
gp  Ein readonly-Feld wird auf eine Objektreferenz aufgerufen, eine const-Konstante hingegen unter Angabe des Klassennamens.

Galileo Computing

4.4.6 Methode oder Eigenschaft?  downtop

Vielleicht werden Sie sich bei den vergangenen Ausführungen die Frage gestellt haben, warum eine relativ komplexe Eigenschaftsmethode angeboten wird. Schließlich könnte man auch über einen herkömmlichen Methodenaufruf einer Instanzvariablen einen Wert zuweisen bzw. diesen abrufen.

Nehmen wir das Beispiel der Eigenschaft XKoordinate in der Klasse Circle. Um den Paradigmen der Objektorientierung zu entsprechen, wird der Wert, den die Eigenschaft beschreibt, in einem privaten Feld gekapselt und über eine Eigenschaftsmethode der Außenwelt zugänglich gemacht.


public class Circle {
  private int xKoordinate = 0;
  ...
  public int XKoordinate {
    get {return xKoordinate;}
    set {xKoordinate = value;}
  }
  ...
}

Nun wollen wir einen alternativen Weg beschreiten. Identisch mit dem eben gezeigten Programmfragment ist nur die private Variable xKoordinate. Um dieser einen Wert zuzuweisen, wird eine Methode SetXKoordinate definiert. Der Parameter newValue empfängt den neuen Wert und weist ihn xKoordinate zu. Die Rückgabe des Eigenschaftswertes erfolgt über die Methode GetXKoordinate.


public class Circle {
  private int xKoordinate = 0;
  ...
  public void SetXKoordinate(int newValue) {
    xKoordinate = newValue;
  }
  public int GetXKoordinate() {
    return xKoordinate;
  }
}

Syntaktisch ist am Code nichts zu beanstanden. Der Aufruf von SetXKoordinate bewirkt, dass dem Feld xKoordinate ein Wert zugewiesen wird, während der Aufruf von GetXKoordinate den Inhalt zurückliefert. Aber diese Variante ist nicht empfehlenswert, und das hat zwei Gründe:

gp  Der Zugriff auf das Feld erfolgt über zwei verschiedennamige Methoden. Würde in allen Klassen so verfahren, wäre der Einarbeitungsaufwand relativ groß, weil die meisten Klassen eine größere Anzahl Felder beschreiben. Zudem verringert sich die Übersichtlichkeit der Klassenfunktionalitäten mit der Anzahl der Klassenmember.
gp  Die Syntax, um einer Eigenschaft einen Wert zuzuweisen, würde anders lauten. Normalerweise erwartet der Aufrufer einer Klasse, unter Angabe des Zuweisungsoperators einer Eigenschaft einen Wert zuzuweisen:
obj.XKoordinate = 100;
    Würde stattdessen eine Methode ohne Rückgabewert implementiert, müsste der Wert als Argument in Klammern übergeben werden:
       
obj.SetXKoordinate(100);

Damit ist klar: Um der allgemein üblichen .NET-Konvention zu folgen und einer Eigenschaft mit dem Zuweisungsoperator einen Wert zuzuweisen bzw. die Eigenschaft auszuwerten, sollten Sie in jedem Fall der Definition einer Eigenschaftsmethode den Vorzug geben.

In der Programmierpraxis verwischen manchmal die Grenzen zwischen einer Eigenschaft und einer Methode. Weil aber im Zweifelsfall die Entscheidung pro oder kontra Eigenschaft bzw. Methode auch eine Entscheidung darüber ist, wie ein Aufrufer das Klassenmitglied aufruft, sollten Sie sich gut überlegen, ob Sie eine Eigenschaft oder eine Methode bereitstellen wollen. Ein allgemein gültiges Kriterium gibt es dabei jedoch nicht.


Galileo Computing

4.4.7 Die Trennung von Daten und Code  downtop

Ein Objekt besteht im Wesentlichen aus Eigenschaften und Methoden. Eigenschaften sind im Grunde genommen nichts anderes als Variablen, also Elemente, die Daten enthalten. So wie für eine int-Variable vier Byte im Speicher reserviert werden, werden auch für ein Feld, das von diesem Datentyp deklariert ist, vier Byte reserviert. Da Objekte meist mehrere Felder (= Eigenschaften) haben, wird für jedes Feld entsprechender Speicher reserviert – allerdings in einem zusammenhängenden Block und nicht wild im Hauptspeicher verstreut.

Typgleiche Objekte reservieren grundsätzlich gleich große Datenblöcke, deren interne Struktur vollkommen identisch aufgebaut ist. Wenn Sie in Ihrem Code die Objektvariable der Klasse Circle deklarieren, wird Speicherbereich in einer Größe reserviert, der alle Zustandsdaten aufnehmen kann. Mit


Circle meinKreis = new Circle();

zeigt die Objektvariable meinKreis auf die Startadresse dieses Datenblocks im Speicher, sie referenziert das Objekt. Daher stammt auch die gebräuchliche Bezeichnung Objektreferenz.

Jedes Objekt beansprucht einen eigenen Datenblock. Diese Notwendigkeit besteht nicht für die Methoden, also den Code einer Klasse. Dieser befindet sich nur einmal »en bloc« im Speicher. Der Code arbeitet zwar mit den Daten eines Objekts, ist aber trotzdem völlig unabhängig von den Objektdaten. Im objektorientierten Sprachgebrauch wird dies auch als die Trennung von Code und Daten bezeichnet. Der Code der Methoden wird nur einmal im Speicher abgelegt und zwar auch dann, wenn noch kein Objekt dieses Typs existiert.


Galileo Computing

4.4.8 Der Zugriff auf private Daten  downtop

Eine Objektmethode kann nicht auf die privaten Daten eines anderen Objekts zugreifen. Dies war bisher immer die Aussage, die allerdings nicht uneingeschränkt gültig ist, wie das folgende Beispiel der Klasse TestClass zeigen soll.


class TestClass {
  private int intVar;
  public void InternProc(TestClass obj) {
    Console.Write("intVar des Objekts = {0}",obj.intVar);
  }
  public int MyValue {
    get {return intVar;}
    set {intVar = value;}
  }
}

In der Klassendefinition ist das Feld intVar private deklariert, um den unbefugten, direkten Zugriff von außen zu unterbinden. Dieses Feld kann infolgedessen nur durch eine Eigenschaft manipuliert werden, in unserem Beispiel MyValue.

Mit etwas Besonderem wartet die Methode InternProc auf. Sie empfängt beim Aufruf im Parameter obj die Referenz auf ein anderes Objekt vom Typ TestClass. Es mag überraschend klingen, aber diese Referenz soll dazu benutzt werden, um auf die private Instanzvariable intVar des übergebenen Objekts zuzugreifen. Nach allen bisherigen Aussagen dürfte dieser Zugriff eigentlich nicht erlaubt sein.

Im folgenden Codefragment wollen wir die Klasse TestClass testen. Dazu werden zwei konkrete Objekte vom Typ TestClass erzeugt und der Eigenschaft MyValue des ersteren ein Wert zugewiesen.


TestClass obj1 = new TestClass();
TestClass obj2 = new TestClass();
obj1.MyValue = 4711;

Im nächsten Schritt folgt der Aufruf der InternProc-Methode des Objekts obj2 unter Übergabe der Referenz auf das Objekt obj1.


obj2.InternProc(obj1);

Tatsächlich wird an der Konsole der Inhalt der gekapselten, privaten Instanzvariablen angezeigt:


intVar des Objekts = 4711

Die Kapselung der Variablen wird also aufgebrochen, die Variable ist auswertbar. Dieser Effekt scheint im Widerspruch zu dem zu stehen, was Sie bisher über die Kapselung gehört haben, und bildet die einzige Ausnahme, die der folgende Satz beschreibt:


Aus einem Objekt heraus kann auf private Instanzvariablen eines anderen Objekts zugegriffen werden, wenn beide Objekte vom gleichen Typ sind.


Galileo Computing

4.4.9 Zusammenfassung  toptop

gp  Zustandsdaten eines Objekts werden in Feldern vorgehalten. Ist ein Feld public deklariert, gehört es zur öffentlichen Schnittstelle eines Objekts und kann ohne Einschränkungen von außen manipuliert werden.
gp  Ein private deklariertes Feld kapselt den von ihm beschriebenen Wert. Der Wert kann nur von Code, der sich innerhalb der Klasse befindet, manipuliert werden.
gp  Standardmäßig enthält eine Eigenschaftsmethode die beiden Accessoren get und set, die jeweils einen eigenen Anweisungsblock beschreiben. Der get-Block wird ausgeführt, wenn ein externer Aufrufer ein bestimmtes Feld auswerten möchte, der set-Block wird bei der Zuweisung an ein Feld aufgerufen.
gp  Fehlt in einer Eigenschaft der set-Accessor, handelt es sich um eine schreibgeschützte Eigenschaft, fehlt der get-Accessor, ist die Eigenschaft lesegeschützt.
gp  Einer der beiden Accessoren kann eine von der Eigenschaftsmethode abweichende Sichtbarkeit haben. Dazu wird dem Accessor ein Zugriffsmodifizierer vorangestellt, der einschränkender sein muss als der der Eigenschaftsmethode.
gp  Konstanten werden im Deklarationsabschnitt einer Klasse festgelegt. Sie ähneln einer Felddefinition, ergänzt um das Schlüsselwort const und den von ihnen repräsentierten Wert. Der Zugriff auf ein const-Feld erfolgt grundsätzlich immer über den Klassenbezeichner.
gp  Eine Konstante kann auch mit dem Schlüsselwort readonly definiert werden. Die Zuweisung an ein readonly-Feld muss nicht in der Deklarationsanweisung stehen, sondern kann auch in einem Konstruktor erfolgen. Im Gegensatz zu einer const-Konstante kann ein readonly-Feld eine Objektreferenz beschreiben.
gp  Aus einem Objekt heraus kann auf private Instanzvariablen eines anderen Objekts zugegriffen werden – vorausgesetzt, beide Objekte sind vom gleichen Typ.
 << zurück
  
  Zum Katalog
Zum Katalog: Visual C# 2005
Visual C# 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Visual Basic 2005






 Visual Basic 2005


Zum Katalog: Java ist auch eine Insel






 Java ist auch eine
 Insel


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de